/**
 * Virtual file system for Monaco Editor in ArenaNext
 * Handles file creation, transformation and module dependencies
 */

// 正则表达式，用于匹配ESM风格导入（带from关键字）
const ESM_IMPORT_REGEX =
  /import\s+(?:(?:\*\s+as\s+\w+)|(?:[\w\s{},]*))?\s+from\s+['"]([^'"]+)['"]/g;

// 正则表达式，用于匹配简单导入语句（不带from关键字）
const SIMPLE_IMPORT_REGEX = /import\s+['"]([^'"]+)['"]/g;

// 正则表达式，用于匹配CJS风格导入（带变量赋值）
const CJS_REQUIRE_REGEX =
  /(?:const|let|var)?\s*(?:[\w\s{},]*)\s*=\s*require\s*\(['"]([^'"]+)['"]\)/g;

// 正则表达式，用于匹配单独的require语句（不带变量赋值）
const SIMPLE_REQUIRE_REGEX = /require\s*\(['"]([^'"]+)['"]\)/g;

// 正则表达式，用于匹配动态导入
const DYNAMIC_IMPORT_REGEX = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;

/**
 * 通过文件名在fileDict中查找文件
 * @param {Object} fileDict - 文件字典
 * @param {string} fileName - 文件名
 * @returns {Object|null} - 找到的文件对象或null
 */
function findFileByName(fileDict, fileName) {
  // 去除文件名中的路径部分和文件扩展名
  let cleanFileName = fileName;

  // 如果包含路径，只取文件名部分
  if (cleanFileName.includes("/")) {
    const parts = cleanFileName.split("/");
    cleanFileName = parts[parts.length - 1];
  }

  // 移除文件扩展名
  cleanFileName = cleanFileName.replace(/\.(js|ts|jsx|tsx)$/, "");

  // 清除所有点号
  cleanFileName = cleanFileName.replace(/\./g, "");

  // 在fileDict中查找匹配的文件
  for (const key in fileDict) {
    if (fileDict.hasOwnProperty(key)) {
      const file = fileDict[key];
      let fileName = file.name;

      // 处理文件名，移除扩展名和点号
      fileName = fileName.replace(/\.(js|ts|jsx|tsx)$/, "").replace(/\./g, "");

      if (fileName === cleanFileName) {
        return file;
      }
    }
  }

  return null;
}

/**
 * 提取代码中的所有导入路径
 * @param {string} code - 源代码
 * @returns {string[]} - 导入路径数组
 */
function extractImportPaths(code) {
  const importPaths = [];
  let match;

  // 匹配ESM风格导入（带from关键字）
  while ((match = ESM_IMPORT_REGEX.exec(code)) !== null) {
    importPaths.push(match[1]);
  }
  // 重置正则表达式的lastIndex
  ESM_IMPORT_REGEX.lastIndex = 0;

  // 匹配简单导入语句（不带from关键字）
  while ((match = SIMPLE_IMPORT_REGEX.exec(code)) !== null) {
    importPaths.push(match[1]);
  }
  // 重置正则表达式的lastIndex
  SIMPLE_IMPORT_REGEX.lastIndex = 0;

  // 匹配CJS风格导入（带变量赋值）
  while ((match = CJS_REQUIRE_REGEX.exec(code)) !== null) {
    importPaths.push(match[1]);
  }
  // 重置正则表达式的lastIndex
  CJS_REQUIRE_REGEX.lastIndex = 0;

  // 匹配单独的require语句（不带变量赋值）
  while ((match = SIMPLE_REQUIRE_REGEX.exec(code)) !== null) {
    // 避免重复添加已经由CJS_REQUIRE_REGEX匹配到的路径
    const path = match[1];
    if (!importPaths.includes(path)) {
      importPaths.push(path);
    }
  }
  // 重置正则表达式的lastIndex
  SIMPLE_REQUIRE_REGEX.lastIndex = 0;

  // 匹配动态导入
  while ((match = DYNAMIC_IMPORT_REGEX.exec(code)) !== null) {
    importPaths.push(match[1]);
  }
  // 重置正则表达式的lastIndex
  DYNAMIC_IMPORT_REGEX.lastIndex = 0;

  return importPaths;
}

/**
 * 规范化导入路径
 * @param {string} path - 原始导入路径
 * @param {string} isClient - 是否为客户端代码
 * @returns {string} - 规范化的文件URI
 */
function normalizeImportPath(path, isClient) {
  // 移除文件扩展名（如果有）
  const pathWithoutExt = path.replace(/\.(js|ts|jsx|tsx)$/, "");

  // 构建基础路径
  const baseDir = isClient ? "client" : "server";

  // 清理路径：移除所有点和斜杠，只保留文件名部分
  let cleanPath = pathWithoutExt;

  // 如果路径包含斜杠，只取最后一部分（文件名）
  if (cleanPath.includes("/")) {
    const parts = cleanPath.split("/");
    cleanPath = parts[parts.length - 1];
  }

  // 移除路径中的所有点号
  cleanPath = cleanPath.replace(/\./g, "");

  // 返回清理后的路径
  return `/${baseDir}/${cleanPath}.js`;
}

/**
 * 为导入创建空模型
 * @param {Object} monaco - Monaco实例
 * @param {string[]} importPaths - 导入路径数组
 * @param {boolean} isClient - 是否为客户端代码
 * @returns {Object[]} - 创建的模型数组
 */
function createModelsForImports(monaco, importPaths, isClient) {
  const createdModels = [];

  // 为每个导入路径创建空模型
  importPaths.forEach((path) => {
    const fileUri = monaco.Uri.file(normalizeImportPath(path, isClient));

    // 检查模型是否已存在
    const existingModel = monaco.editor.getModel(fileUri);
    if (!existingModel) {
      const model = monaco.editor.createModel("", "javascript", fileUri);
      createdModels.push(model);
    } else {
      createdModels.push(existingModel);
    }
  });

  return createdModels;
}

/**
 * 清理未使用的模型
 * @param {Object} monaco - Monaco实例
 */
function cleanupUnusedModels(monaco) {
  // 获取所有模型
  const allModels = monaco.editor.getModels();

  // 销毁文件模型，让它们在下次访问时重新创建
  allModels.forEach((model) => {
    const modelUri = model.uri.toString();

    if (modelUri.includes("file:///")) {
      model.dispose();
    }
  });
}

// 跟踪当前选择的文件ID，避免重复处理
let currentSelectedFileId = "";

/**
 * Updates the virtual file system for the Monaco editor
 * @param {Object} monacoController - The Monaco controller
 * @param {Object} editorState - The editor state with file dictionary
 */
export function updateVirtualFileSystem(monacoController, editorState) {
  const selectedFileId = editorState.selectedFileId;

  // 如果选中文件没有变化，不执行后续操作
  if (currentSelectedFileId === selectedFileId) {
    return;
  }

  // 更新当前选中文件ID
  currentSelectedFileId = selectedFileId;

  const fileDict = editorState.fileDict;
  const monaco = monacoController.monacoController.monaco;

  // 首先清理现有模型
  cleanupUnusedModels(monaco);

  if (!selectedFileId || !fileDict[selectedFileId]) {
    return;
  }

  // 提取当前选中文件的信息
  const { name, text, isClient } = fileDict[selectedFileId];

  // 设置编译器基础路径
  const baseUrl = isClient ? "file:///client" : "file:///server";
  const dtsContent = [
    {
      filePath: isClient ? "ClientAPI.d.ts" : "GameAPI.d.ts",
      content:
        monaco.languages.typescript.javascriptDefaults.getExtraLibs()[
          "api.d.ts"
        ].content,
    },
  ];
  monaco.languages.typescript.typescriptDefaults.setExtraLibs(dtsContent);
  monaco.languages.typescript.javascriptDefaults.setExtraLibs(dtsContent);

  const compilerOptions = {
    ...monaco.languages.typescript.javascriptDefaults.getCompilerOptions(),
    lib: isClient ? ["es2015"] : [],
    baseUrl,
    rootDir: baseUrl,
  };
  monaco.languages.typescript.javascriptDefaults.setCompilerOptions(
    compilerOptions
  );
  monaco.languages.typescript.typescriptDefaults.setCompilerOptions(
    compilerOptions
  );

  // 提取当前文件的导入路径
  const importPaths = extractImportPaths(text);

  // 记录已经创建过模型的文件名列表
  const fileNameList = [];

  // 先为导入的文件创建模型
  importPaths.forEach((path) => {
    const file = findFileByName(fileDict, path);
    if (file) {
      // 为当前文件创建模型
      const currentFileUri = monaco.Uri.file(
        normalizeImportPath(file.name, isClient)
      );

      monaco.editor.createModel(file.text, "javascript", currentFileUri);
      fileNameList.push(file.name);
    }
  });

  // 为fileDict中其他未注册的文件创建模型
  // 这样可以确保所有文件都能被正确解析
  for (const key in fileDict) {
    if (fileDict.hasOwnProperty(key)) {
      const file = fileDict[key];

      // 跳过当前选中的文件和已经创建过模型的文件
      if (key === selectedFileId || fileNameList.includes(file.name)) {
        continue;
      }

      // 为未注册的文件创建模型
      const fileUri = monaco.Uri.file(
        normalizeImportPath(file.name, file.isClient)
      );

      // 检查模型是否已存在
      const existingModel = monaco.editor.getModel(fileUri);
      if (!existingModel) {
        monaco.editor.createModel(file.text, "javascript", fileUri);
      }
    }
  }
}
